#include "fiber.h"
#include <atomic>
#include <cstdint>
#include "scheduler.h"  // 协程调度器（用于管理多个协程的调度）
#include "utils.h"      // 工具函数（如断言、日志等）

namespace fiber 
{
const bool DEBUG = true;  // 调试开关（实际生产环境可关闭）

// 当前线程正在运行的协程（每个线程独立维护）
static thread_local Fiber* cur_fiber = nullptr;

// 当前线程的"主协程"（线程启动时默认的协程，负责调度其他子协程）
static thread_local Fiber::ptr cur_thread_fiber = nullptr;

// 全局原子变量：用于生成协程唯一ID（原子操作确保多线程安全）
static std::atomic<uint64_t> cur_fiber_id{0};

// 全局原子变量：统计系统中当前存在的协程总数
static std::atomic<uint64_t> fiber_count{0};

// 协程默认栈大小（128KB，用户态协程栈通常比线程栈小得多）
// 仅在当前文件中有意义
static int g_fiber_stack_size = 128 * 1024;

// 栈内存分配器（封装栈的申请与释放，便于后续替换为更高效的分配策略）
class StackAllocator {
public:
    // 分配栈内存（使用malloc，简单但可能有内存碎片问题）
    static void *Alloc(size_t size) { return malloc(size); }
    // 释放栈内存（对应malloc的free）
    static void Delete(void *vp, size_t size) { return free(vp); }
};


// 私有构造函数：仅用于创建当前线程的"主协程"
// 主协程是线程启动时默认运行的协程，负责调度其他子协程
Fiber::Fiber() {
    SetThis(this);  // 将当前协程设为线程的"当前运行协程"
    m_state = State::RUNNING;  // 主协程初始状态为"运行中"

    // 获取当前上下文（保存CPU寄存器、栈指针等状态到ctx_）
    // 若失败则触发断言（CondPanic是自定义断言，失败时终止程序）
    CondPanic(getcontext(&m_ctx) == 0, "getcontext error");

    fiber_count++;  // 协程总数+1
    m_id = cur_fiber_id++;  // 分配唯一ID（从0开始递增）
    std::cout << "[fiber] create fiber , id = " << m_id << std::endl;
}

// 公有构造函数：创建"子协程"（用户任务对应的协程）
Fiber::Fiber(std::function<void()> cb, size_t stacksize, bool run_inscheduler)
: m_id(cur_fiber_id++), m_cb(cb), m_isRunInScheduler(run_inscheduler) {
    fiber_count++;  // 协程总数+1
    
    // 协程的状态在最开始就已经设置为READY了，不需要在构造的时候设置

    // 确定栈大小（用户指定或默认）
    m_stackSize = stacksize > 0 ? stacksize : g_fiber_stack_size;
    // 分配栈内存（通过StackAllocator）
    m_stack_ptr = StackAllocator::Alloc(m_stackSize);

    // 获取当前上下文（作为子协程的初始上下文）
    CondPanic(getcontext(&m_ctx) == 0, "getcontext error");

    // 初始化协程上下文：
    m_ctx.uc_link = nullptr;    // 协程执行结束后无需自动切换到其他上下文
    m_ctx.uc_stack.ss_sp = m_stack_ptr;     // 协程栈的起始地址
    m_ctx.uc_stack.ss_size = m_stackSize;   // 协程栈的大小

    // 设置协程入口函数为MainFunc（协程启动时执行此函数）
    makecontext(&m_ctx, &Fiber::MainFunc, 0);

    std::cout << "create son fiber , id = " << m_id << std::endl;
}

// 析构函数：释放协程资源（栈空间、更新计数器）
Fiber::~Fiber() {
    fiber_count--;  // 协程总数-1

    if (m_stack_ptr) {
        // 有栈空间 → 子协程：必须确保已结束（否则可能内存泄漏）
        CondPanic(m_state == State::TERM, "fiber state should be term");
        // 释放栈内存
        StackAllocator::Delete(m_stack_ptr, m_stackSize);
    } 
    else {
        // 无栈空间 说明是：主协程
        CondPanic(!m_cb, "main fiber no callback");  // 主协程不应有任务函数
        CondPanic(m_state == State::RUNNING, "main fiber state should be running");  // 主协程生命周期与线程一致

        // 清理当前协程引用
        Fiber *cur = cur_fiber;
        if (cur == this) {
            SetThis(nullptr);   // 确保线程局部存储中 cur_fiber 不再指向已被释放的内存
        }
    }
}

// 设置当前线程正在运行的协程（更新TLS变量cur_fiber）
void Fiber::SetThis(Fiber *f) {
    cur_fiber = f;
}

// 重置协程（复用已结束的协程栈空间，重新绑定新任务）
// 用途：避免频繁分配/释放栈内存（栈分配成本较高）
void Fiber::reset(std::function<void()> cb) {
    CondPanic(m_stack_ptr, "stack is nullptr");  // 断言：必须有栈空间（子协程）
    CondPanic(m_state == State::TERM, "state isn't TERM");  // 断言：只能重置已结束的协程

    m_cb = cb;  // 绑定新的任务函数

    // 重新初始化上下文（复用旧栈）
    CondPanic(0 == getcontext(&m_ctx), "getcontext failed");
    m_ctx.uc_link = nullptr;
    m_ctx.uc_stack.ss_sp = m_stack_ptr;  // 复用原栈空间
    m_ctx.uc_stack.ss_size = m_stackSize;

    // 重新设置入口函数为MainFunc
    makecontext(&m_ctx, &Fiber::MainFunc, 0);
    m_state = State::READY;  // 重置后状态为"就绪态"
}

// 恢复当前协程运行（从"就绪态"切换到"运行态"）
// 逻辑：保存当前运行协程的上下文，切换到本协程的上下文
void Fiber::resume() {
    // 断言：协程必须处于"就绪态"（READY）才能被恢复（避免重复运行或操作已结束的协程）
    // 避免 “未定义状态” 的隐患
    CondPanic(m_state != State::TERM && m_state != State::RUNNING, "state error");

    SetThis(this);  // 将当前协程设为线程的"当前运行协程"
    m_state = State::RUNNING;  // 更新状态为"运行中"

    if (m_isRunInScheduler) {
        // 若协程由调度器管理，切换到调度协程上下文
        // Scheduler::GetSchedulerFiber()：获取调度协程（负责调度的核心协程）
        CondPanic(0 == swapcontext(&(Scheduler::GetSchedulerFiber()->m_ctx), &m_ctx),
                "m_isRunInScheduler = true, swapcontext error");
    } 
    else {
        // 若协程手动管理，切换到线程的主协程上下文
        CondPanic(0 == swapcontext(&(cur_thread_fiber->m_ctx), &m_ctx), 
                "m_isRunInScheduler = false, swapcontext error");
    }
}


// 当前协程让出执行权（从"运行态"切换到"就绪态"）
// 逻辑：保存本协程的上下文，切换回主协程或调度器协程
void Fiber::yield() {
    // 断言：只有"运行中"或"已结束"的协程才能让出（避免无效操作）
    CondPanic(m_state == State::TERM || m_state == State::RUNNING, "state error");

    // 切换当前运行协程为线程主协程
    SetThis(cur_thread_fiber.get());

    if (m_state != State::TERM) {
        // 若协程未结束，更新状态为"就绪态"（等待下次被resume）
        m_state = State::READY;
    }

    if (m_isRunInScheduler) {
        // 若由调度器管理，切换回调度协程
        CondPanic(0 == swapcontext(&m_ctx, &(Scheduler::GetSchedulerFiber()->m_ctx)),
                "isRunInScheduler_ = true,swapcontext error");
    } 
    else {
        // 若手动管理，切换回线程主协程
        CondPanic(0 == swapcontext(&m_ctx, &(cur_thread_fiber->m_ctx)), "swapcontext failed");
    }
}


// 协程入口函数（所有子协程的执行起点）
// 作用：封装用户任务的执行，执行完毕后自动切换状态并让出执行权
void Fiber::MainFunc() {
    // 获取当前协程的智能指针（确保协程在执行期间不被析构）
    Fiber::ptr cur = GetThis();
    CondPanic(cur != nullptr, "cur is nullptr");

    // 执行用户传入的任务函数（cb_是协程的核心逻辑）
    cur->m_cb();
    // 任务执行完毕，清空回调（避免重复执行）
    cur->m_cb = nullptr;
    // 更新状态为"已结束"（TERM）
    cur->m_state = State::TERM;

    // 手动减少智能指针引用计数（准备释放资源）
    auto raw_ptr = cur.get();  // 保存原始指针（避免reset后失效）
    cur.reset();  // 当前函数释放智能指针的引用

    // 协程结束后，自动让出执行权（切换回主协程或调度器）
    // 注意：此时只能用raw_ptr，因为cur已被reset
    raw_ptr->yield();
}

// 获取当前线程正在运行的协程（若不存在则创建主协程）
Fiber::ptr Fiber::GetThis() {
    if (cur_fiber) {
        // 若已有当前协程，返回其智能指针（shared_from_this确保生命周期管理）
        return cur_fiber->shared_from_this();
    }

    // 若当前线程无协程（首次调用），创建主协程
    // 这里调用无参构造，里面自动设置了当前的协程为创建的主协程
    Fiber::ptr main_fiber(new Fiber);
    // 断言：确保主协程被正确设置为当前协程
    CondPanic(cur_fiber == main_fiber.get(), "cur_fiber need to be main_fiber");
    // 保存主协程的智能指针（避免被析构）
    cur_thread_fiber = main_fiber;
    return cur_fiber->shared_from_this();
}

uint64_t Fiber::TotalFiberNum() {
    return fiber_count;  // fiber_count 是全局 atomic<uint64_t> 变量
}

uint64_t Fiber::GetCurFiberID() {
    // 确保当前有运行的协程，否则可能返回 0 或触发断言
    if (cur_fiber) {
        return cur_fiber->m_id;
    }
    return 0;  // 或通过 CondPanic 断言检查
}

}  // namespace fiber